#pragma once 

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <cstdlib>
#include <fstream>
#include <memory>
#include <sstream>
#include <string>
#include <boost/algorithm/string.hpp>
#include <unistd.h>
#include <utility>
#include <vector>
#include <log.h>
#include <cstdlib>

#include "Util.hpp"

namespace lyt{

    class Runner{
    public:
        using ptr = std::shared_ptr<Runner>;
        Runner(){}
        bool init(const std::string& image_name, const std::string& dockerfile_path) {
            _image_name = image_name;
            std::stringstream ss;
            std::string exists_file = "./docker_image_exists.log";
            // 检查镜像是否存在
            ss << "docker images -q ";
            ss << _image_name ;
            ss << " >" << exists_file;
            bool ret = systemcall(ss.str());
            std::string exists_str = FileUtil::ReadText(exists_file);
            debuglog << "exists:" << exists_str;
            if (exists_str.empty() == false) return true;
            // docker build -t online-judge-runner-image ./
            ss.str("");
            ss << "docker build -t ";
            ss << image_name;
            ss << " ";
            ss << dockerfile_path;
            return systemcall(ss.str());
        }
        /**
         * @brief 
         * 
         * @param relationship src_path:docker_work_path (src_path 源码所在路径，不包含源码文件名， socker_work_path ：docker中的工作目录)
         * @return true 
         * @return false 
         */
        bool compile(const std::string& relationship,
            const std::string& src_file,
            const std::string& exec_file,
            const std::string&error_file
        ) {
            // docker run --rm --log-driver=none -v ./app:/app cpp-runner bash -c 'g++ -std=c++17 -O2 -Wall -o main main.cpp'  >error.txt 2>&1
            std::stringstream ss;
            ss << "docker run --rm --log-driver=none -v ";
            ss << relationship;
            ss << " " << _image_name;
            ss << " bash -c \"sh -c 'g++ -std=c++17 -O2 -Wall -o ";
            ss << exec_file;
            ss << " ";
            ss << src_file;
            ss << " >";
            ss << error_file;
            ss << " 2>&1'\"";
            bool ret = systemcall(ss.str());
            return ret;
        }
        /**
         * @brief 
         * 
         * @param relationship src_path:docker_work_path (src_path 源码所在路径，不包含源码文件名， socker_work_path ：docker中的工作目录)
         * @return true 
         * @return false 
         */
        int run(const std::string& relationship,
            const std::string& exec_file, 
            const std::string& input_file,
            const std::string& output_file, 
            const std::string& error_file,
            const std::string& quit_code_file
        ) {
            // docker run --rm --ulimit cpu=2 --memory=128m --cpu-quota=50000 --network=none  -v ./app:/app cpp-runner bash -c 
            // 'sh -c ./main < input.txt > output.txt 2> error.txt'
            std::stringstream ss;
            ss << "docker run --rm --ulimit cpu=2 --memory=128m --cpu-quota=50000 --network=none -v ";
            ss << relationship;
            ss << " " << _image_name;
            ss << " bash -c 'sh -c ./" + exec_file;
            ss << " < " << input_file;
            ss << " > ";
            ss << output_file;
            ss << " 2>>" << error_file << "';echo $? ";// 139 = 128 + 11 (11就是段错误、栈溢出退出码)
                                                    // 152 = 128 + 24 (超时)
                                                    // 136 = 128 + 8（浮点数错误）
                                                    // 134 = 128 + 6（abort）
                                                    //
            ss << " > " + quit_code_file; 
            int status = 0;
            bool ret = systemcall(ss.str(),&status);
            if (ret) {
                return 0; // 运行成功
            }
            return status & 0x7f;
        }

    private:

        bool systemcall(const std::string& command_str,int * result = nullptr) {
            int status = std::system(command_str.data());
            if (result){
                *result = status;
            }
            if (status & 0x7f) {
                return false;
            }
            if ((status >> 8) & 0xff){
                
                return false;
            }
            return true;
        }
    private:
        std::string _image_name;
    };
}